/*
 * Copyright 2007 Transmogrify, LLC.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.xmog.web.servlet;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.log4j.Logger;

import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.matcher.Matchers;
import com.xmog.web.annotation.processing.AnnotationProcessor;
import com.xmog.web.handler.UrlHandler;
import com.xmog.web.persistence.TransactionInterceptor;
import com.xmog.web.persistence.Transactional;
import com.xmog.web.view.Forward;
import com.xmog.web.view.Redirect;

/**
 * Main framework servlet for handling web requests.
 * 
 * @author <a href="mailto:maa@xmog.com">Mark Allen</a>
 */
public class DispatcherServlet extends HttpServlet {
	private Map<String, UrlHandler> handlerMap = new HashMap<String, UrlHandler>();
	private Injector injector;

	private static final Logger log = Logger.getLogger(DispatcherServlet.class);

	/**
	 * @see javax.servlet.GenericServlet#init(javax.servlet.ServletConfig)
	 */
	@Override
	@SuppressWarnings("unchecked")
	public void init(ServletConfig config) throws ServletException {
		super.init(config);
		log.info("Intializating Xmog Web...");
		long time = System.currentTimeMillis();
		String basePackage = config.getInitParameter("basePackage");
		handlerMap = new AnnotationProcessor(basePackage).getHandlerMap();

		// TODO: we need a method for allowing users to
		// specify their own modules without resorting to something like XML
		// configuration
		injector = Guice.createInjector(new AbstractModule() {
			@Override
			protected void configure() {
				bindInterceptor(Matchers.any(), Matchers.annotatedWith(Transactional.class),
						new TransactionInterceptor());
			}
		});

		log.info("Initialization completed in " + (System.currentTimeMillis() - time) + "ms.");
	}

	/**
	 * @see javax.servlet.http.HttpServlet#doGet(javax.servlet.http.HttpServletRequest,
	 *      javax.servlet.http.HttpServletResponse)
	 */
	@Override
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException,
			IOException {
		String requestUri = request.getRequestURI().substring(request.getContextPath().length());
		UrlHandler handler = handlerMap.get(requestUri);

		if (handler == null) {
			log.debug("No handler registered for " + requestUri + ", forwarding to 404 page...");
		} else {
			log.debug("Requests for " + requestUri + " are handled by " + handler + ", invoking...");

			try {
				WebContext.setRequest(request);
				WebContext.setResponse(response);

				Object instance = injector.getInstance(handler.getDeclaringClass());
				Object result = handler.invoke(instance);
				if (result != null) {
					if (result.getClass().isAssignableFrom(Forward.class)) {
						String forward = ((Forward) result).getName();
						log.debug("Forwarding to " + forward + "...");
						request.getRequestDispatcher(forward).forward(request, response);
					} else if (result.getClass().isAssignableFrom(Redirect.class)) {
						String redirect = ((Redirect) result).getName();
						log.debug("Redirecting to " + redirect + "...");
						response.sendRedirect(redirect);
					}
				}
			} catch (Throwable t) {
				log.error("Error occurred in handler.", t);
			} finally {
				WebContext.cleanup();
			}
		}
	}

	/**
	 * @see javax.servlet.http.HttpServlet#doPost(javax.servlet.http.HttpServletRequest,
	 *      javax.servlet.http.HttpServletResponse)
	 */
	@Override
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException,
			IOException {
		super.doPost(request, response);
	}
}
